//********************************************************************************************************
// Product Name: MapWindow.dll Alpha
// Description:  The core libraries for the MapWindow 6.0 project.
//
//********************************************************************************************************
// The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); 
// you may not use this file except in compliance with the License. You may obtain a copy of the License at 
// http://www.mozilla.org/MPL/ 
//
// Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF 
// ANY KIND, either expressed or implied. See the License for the specificlanguage governing rights and 
// limitations under the License. 
//
// The Original Code is MapWindow.dll for the MapWindow 6.0 project
//
// The Initial Developer of this Original Code is Ted Dunsford. Created in January, 2008.
// 
// Contributor(s): (Open source contributors should list themselves and their modifications here). 
//
//********************************************************************************************************
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
namespace MapWindow.Components
{
    /// <summary>
    /// This is a convenient structure for organizing the 3D view information
    /// </summary>
    public struct Camera
    {
        private float _aspect;
        private Vector3 _cameraLocation;
        private Vector3 _cameraUpDirection;
        private Vector3 _TargetLocation;
        private Vector3 _InitialCameraLocation;
        private Vector3 _InitialCameraUpDirection;
        private Vector3 _InitialTargetLocation;
        private Matrix _WorldMatrix;
        private float _minimumDistance;
        private float _maximumDistance;
        

        #region Methods


        /// <summary>
        /// Resets the view to whatever the initial values are
        /// </summary>
        public void Reset()
        {
            
            _TargetLocation = _InitialTargetLocation;
            _cameraLocation = _InitialCameraLocation;
            _cameraUpDirection = _InitialCameraUpDirection;
            _minimumDistance = .00001f;
            _maximumDistance = 1000f;
            _WorldMatrix = Matrix.Identity;
        }

        /// <summary>
        /// Ratio is the ratio of the new distance to the previous distance.
        /// </summary>
        /// <param name="ratio">A float describing the factor to modify the distance by</param>
        public void Zoom(float ratio)
        {
            Vector3 v = Vector3.Subtract(_cameraLocation, _TargetLocation);
            v.Multiply(ratio);
            _cameraLocation = Vector3.Add(_TargetLocation, v);
        }

        /// <summary>
        /// Given a device, this will position the camera for that device by adjusting the appropriate matrix values
        /// </summary>
        /// <param name="device"></param>
        public void Position(Device device)
        {
            device.Transform.World = _WorldMatrix;
            device.Transform.View = Matrix.LookAtLH(_cameraLocation, _TargetLocation, _cameraUpDirection);
            if (this.View.Length() < Math.Abs(_cameraLocation.Z))
            {
                _maximumDistance = Math.Abs(_cameraLocation.Z * 5);
            }
            else
            {
                _maximumDistance = Math.Abs(this.View.Length() * 5);
            }
            _minimumDistance = _maximumDistance / 100;
           
            device.Transform.Projection = Matrix.PerspectiveFovLH((float)Math.PI / 4, _aspect, _minimumDistance, _maximumDistance);
        }


        #endregion


        #region Properties

        /// <summary>
        /// This is the width of the control divided by the height of the control
        /// </summary>
        public float Aspect
        {
            get { return _aspect; }
            set { _aspect = value; }
        }

        /// <summary>
        /// Controls the three dimensional position of the camera that is viewing the world
        /// </summary>
        public Vector3 CameraLocation
        {
            get { return _cameraLocation; }
            set { _cameraLocation = value; }
        }

        /// <summary>
        /// Controls the direction that coresponds to directly "up" for the camera
        /// </summary>
        public Vector3 CameraUpDirection
        {
            get { return _cameraUpDirection; }
            set { _cameraUpDirection = value; }
        }

        /// <summary>
        /// Used to help reset the view
        /// </summary>
        public Vector3 InitialCameraLocation
        {
            get { return _InitialCameraLocation; }
            set { _InitialCameraLocation = value; }
        }

        /// <summary>
        /// Used to help reset the view
        /// </summary>
        public Vector3 InitialCameraUpDirection
        {
            get { return _InitialCameraUpDirection; }
            set { _InitialCameraUpDirection = value; }
        }

        /// <summary>
        /// Used to help reset the view
        /// </summary>
        public Vector3 InitialTargetLocation
        {
            get { return _InitialTargetLocation; }
            set { _InitialTargetLocation = value; }
        }

        /// <summary>
        /// Specifies the near plane distance from the camera
        /// </summary>
        public float MinimumDistance
        {
            get { return _minimumDistance; }
            set { _minimumDistance = value; }
        }

        /// <summary>
        /// Specifies the far plane distance from the camera
        /// </summary>
        public float MaximumDistance
        {
            get { return _maximumDistance; }
            set { _maximumDistance = value; }
        }

        /// <summary>
        /// This is a target location for the view to be focusing on.
        /// </summary>
        public Vector3 TargetLocation
        {
            get { return _TargetLocation; }
            set { _TargetLocation = value; }
        }

        /// <summary>
        /// This returns a plane that is normal to the view direction
        /// and intersects the target point.
        /// </summary>
        public Plane TargetPlane
        {
            get
            {
                Vector3 v = Vector3.Subtract(_cameraLocation, _TargetLocation);
                return new Plane(v.X, v.Y, v.Z, v.Length());

                
            }
        }

        /// <summary>
        /// Obtains a vector describing both the distance and direction from the camera to the target
        /// </summary>
        public Vector3 View
        {
            get
            {
                return Vector3.Subtract(_TargetLocation, _cameraLocation);
            }
        }

        



        /// <summary>
        /// This is the matrix to use in order to scale or alter the world
        /// </summary>
        public Matrix WorldMatrix
        {
            get { return _WorldMatrix; }
            set { _WorldMatrix = value; }
        }

      


        #endregion

    }

    
}
